#ifdef ENABLE_MAPLE_SAN

#ifndef MAPLE_SAN_ASAN_UD_H
#define MAPLE_SAN_ASAN_UD_H

#include "asan_config.h"
#include "asan_function.h"
#include "asan_mapping.h"
#include "me_cfg.h"
#include "me_function.h"
#include "me_ssa.h"
#include "mir_module.h"
#include "mpl_logging.h"

// Handle asan struct 

#include "san_common.h"
#include "asan_interfaces.h"
#include "me_ir.h"
#include "mir_builder.h"
#include "string_utils.h"



namespace maple {

typedef uint8_t OpCodeID; 
typedef int32_t RegID; 
typedef uint32_t VarID; 

// Struct to contain the information for performing set check
// For elimination of san check
struct set_check {
  std::vector<OpCodeID> opcode;             // 1. opcode  enum Opcode : uint8
  std::vector<RegID> register_terminal;  // 2. register_terminal -> cannot further expand
  std::stack<RegID> register_live;       // 3. register_live  -> for further expansion
  std::vector<VarID> var_terminal;      // 4. var_terminal -> cannot further expand
  std::stack<VarID> var_live;           // 5. var_live  -> for further expansion
  std::vector<IntVal> const_int64;       // 6. const, we only track int64 or kConstInt
  std::vector<uint32_t> const_str;         // 7. const str ,we only store the index
  std::vector<int32_t> type_num;  // 8. Type, but we will just track the fieldID for simplicity  iread->GetFieldID()
  std::vector<StmtID> dassignID;  // 9. stmtID, we only track the stmtID for simplicity
};

struct san_struct {
  StmtID stmtID;
  AsanRazorCheckCount tot_ctr;
  AsanRazorCheckCount false_ctr;//False 
  AsanRazorCheckCount true_ctr;//True
};

// Helper function 
template <typename T>
void print_stack(std::stack<T> &st) {
  if (st.empty()) return;
  T x = st.top();
  LogInfo::MapleLogger() << x << ",";
  st.pop();
  print_stack(st);
  st.push(x);
}

template<typename K, typename V>
std::vector<K> inverseLookup(const std::map<K, V>& map, const V& value) {
    std::vector<K> keys;
    // Iterate through the map
    for (const auto& pair : map) {
        if (pair.second == value) {
            keys.push_back(pair.first);
            break;
        }
    }
    return keys;
}

class UDProfile {

 public:
  UDProfile();
  
  void initME(MeFunction &mefunc, std::set<StmtID> asanStmtIDSet);
  void GetUD(StmtNode &stmt);
  void Dump(); 
  void GetMatched();
  set_check* GetSetCheck(StmtID stmtID);
  bool sat_check(const set_check& a, const set_check& b);
  bool dynamic_sat(const san_struct& a, const san_struct& b, bool SCSC);

  std::map<StmtID, set_check> dassignID2dep;
  void CreateDassignIDMap();

  bool UnmatchUserCheck(StmtID UcStmtID, std::set<StmtID> asanStmtIDSet);
  // san_struct Get_san_struct(StmtID id); 

  template <typename T>
  bool compareVectors(const std::vector<T>& a, const std::vector<T>& b) {
    // I am not sure why the original implementation use
    // sets to compare the equivalence of two vectors (peformance?)
    // Anyway, I think we may not delete the following code now
    // if (a.size() != b.size())
    // {
    //    return false;
    // }
    // std::sort(a.begin(), a.end());
    // std::sort(b.begin(), b.end());
    // return (a == b);
    std::set<T> set_a(a.begin(), a.end());
    std::set<T> set_b(b.begin(), b.end());
    return (set_a == set_b);
  }

  set_check commit(set_check old, set_check latest) {
    old.opcode.insert(old.opcode.end(), latest.opcode.begin(), latest.opcode.end());
    old.register_terminal.insert(old.register_terminal.end(), latest.register_terminal.begin(),
                                latest.register_terminal.end());
    old.var_terminal.insert(old.var_terminal.end(), latest.var_terminal.begin(), latest.var_terminal.end());
    old.const_int64.insert(old.const_int64.end(), latest.const_int64.begin(), latest.const_int64.end());
    old.const_str.insert(old.const_str.end(), latest.const_str.begin(), latest.const_str.end());
    old.type_num.insert(old.type_num.end(), latest.type_num.begin(), latest.type_num.end());
    old.dassignID.insert(old.dassignID.end(), latest.dassignID.begin(), latest.dassignID.end());
    return old;
  }

  static void set_check_print_dep(set_check dep) {
    LogInfo::MapleLogger() << "\nTerm Var: ";
    for (auto var_tmp : dep.var_terminal) {
      LogInfo::MapleLogger() << int(var_tmp) << ",";
    }
    // LogInfo::MapleLogger() << "\nConst: ";
    // for (auto var_tmp : dep.const_int64) {
    //   LogInfo::MapleLogger() << var_tmp.GetRawData() << ",";
    // }
    LogInfo::MapleLogger() << "\n";
  }

 private:

  MeFunction *mefunc;

  std::set<RegID> reg_order;
  std::map<RegID, std::vector<StmtNode *>> reg_to_stmt;

  std::set<VarID> var_order;
  std::map<VarID, std::vector<StmtNode *>> var_to_stmt;

  std::map<StmtID, set_check> StmtID2Check;
  
  // The std map from asan razor 
  std::set<StmtID> asanStmtIDSet;

  // UD stuff 
  /* We would like to collect the use and define info
  by traversing the IR with sth 'above' it.
  */
  void recursion(BaseNode *stmt, std::vector<VarID> &stmt_reg,
                                 std::vector<IntVal> &stmt_const);
  StmtNode *retLatest_Regassignment(StmtNode *stmt, int32 register_number);
  StmtNode *retLatest_Varassignment(StmtNode *stmt, uint32 var_number);
  void dep_iassign_expansion(IassignNode *iassign, set_check &dep);
  void dep_constval_expansion(ConstvalNode *constValNode, set_check &dep);
  void dep_dassign_expansion(DassignNode *dassign, set_check &dep);
  void gen_register_dep(StmtNode *stmt, set_check &br_tmp);
  void dep_expansion(BaseNode *stmt, set_check &dep);


  bool IsCallNode(Opcode opcode) {
    // Check if it is a callnode statement
    switch (opcode) {
      case OP_call:
      case OP_virtualcall:
      case OP_virtualicall:
      case OP_superclasscall:
      case OP_interfacecall:
      case OP_interfaceicall:
      case OP_customcall:
      case OP_polymorphiccall:
      case OP_callassigned:
      case OP_virtualcallassigned:
      case OP_virtualicallassigned:
      case OP_superclasscallassigned:
      case OP_interfacecallassigned:
      case OP_interfaceicallassigned:
      case OP_customcallassigned:
      case OP_polymorphiccallassigned:
        return true;
      default:
        return false;
    }
  }
};

}

#endif  // MAPLE_SAN_ASAN_UD_H

#endif  // ENABLE_MAPLE_SAN